#include <flat.h>

Flat::Flat() : flat(0), flatHeight(0), flatWidth(0) {
}

Flat::~Flat() {
	destroy();
}

const bool Flat::create(const string path) {
	
	istream *in = &cin;

	if(path != "")
		in = new ifstream(path.c_str());

	create(*in);

	if(path != "")
		delete in;
}

const bool Flat::create(istream &in) {
	if(!(in >> flatHeight)) return false;
	if(!(in >> flatWidth)) return false;

	flat = new Tile*[flatHeight];

	for(int i = 0; i < flatHeight; i++) flat[i] = 0;

	for(int i = 0; i < flatHeight; i++) {
		flat[i] = new Tile[flatWidth];
		for(int j = 0; j < flatWidth; j++) {
			int aux(0); if(!(in >> aux)) return false;
			switch(aux) {
				case FILE_WALL:
					flat[i][j].setWall();
					break;
				case FILE_IMPORTANT_TILE:
					flat[i][j].setImportant();
					break;
			}
		}
	}

	return true;
}

void Flat::destroy() {
	if(flat != 0)
	{
		for(int i = 0; i < flatHeight; i++)
			if(flat[i] != 0)
				delete[] flat[i];
		delete[] flat;

		flat = 0;
	}
}

const bool Flat::check() const {
	for(int i = 0; i < flatHeight; i++) {
		for(int j = 0; j < flatWidth; j++) {
			if(flat[i][j].isImportant()) {

				/* Verifica que no existan paredes enfrentadas en las baldosas importantes. */
				if((j > 0) && (j < (flatWidth-1)) && (flat[i][j-1].isWall() && flat[i][j+1].isWall())) return false;
				if((j > 0) && (j == (flatWidth-1)) && (flat[i][j-1].isWall())) return false;
				if((j == 0) && (j < (flatWidth-1)) && (flat[i][j+1].isWall())) return false;

				if((i > 0) && (i < (flatHeight-1)) && (flat[i-1][j].isWall() && flat[i+1][j].isWall())) return false;
				if((i > 0) && (i == (flatHeight-1)) && (flat[i-1][j].isWall())) return false;
				if((i == 0) && (i < (flatHeight-1)) && (flat[i+1][j].isWall())) return false;
			}
		}
	}

	return true;
}

const Coordinate Flat::getAvailableTile(const Coordinate& c) const {
	for(int j = c.x; j < flatWidth; j++) {
		if(flat[c.y][j].isAvailable())
			return Coordinate(j, c.y);
	}

	for(int i = (c.y + 1); i < flatHeight; i++)
		for(int j = 0; j < flatWidth; j++) {
			if(flat[i][j].isAvailable())
				return Coordinate(j, i);
		}

	return CoordinateNIL;
}

const Coordinate Flat::getNextCoordinate(const Coordinate &c) const {
	
	Coordinate newCoord(c);

	newCoord.x += 1;

	if(newCoord.x == flatWidth) {
		newCoord.x = 0;
		newCoord.y += 1;
		if(newCoord.y == flatHeight)
			newCoord = CoordinateNIL;
	}

	return newCoord;
}

const bool Flat::isValidTile(const Coordinate &c, Sensor s) const {

	if(s == SENSOR_TH || s == SENSOR_FD) {
		
		for(int j = (c.x + 1); j < flatWidth; j++) {

			if(flat[c.y][j].isWall())
				break;

			if(flat[c.y][j].hasSensor())
				return false;
		}

		for(int j = (c.x - 1); j > -1; j--) {

			if(flat[c.y][j].isWall())
				break;

			if(flat[c.y][j].hasSensor())
				return false;
		}
	}

	if(s == SENSOR_TV || s == SENSOR_FD)
	{
		for(int i = (c.y + 1); i < flatHeight; i++) {

			if(flat[i][c.x].isWall())
				break;

			if(flat[i][c.x].hasSensor())
				return false;
		}

		for(int i = (c.y - 1); i > -1; i--) {

			if(flat[i][c.x].isWall())
				break;
			
			if(flat[i][c.x].hasSensor())
				return false;
		}
	}

	return true;
}

const bool Flat::isCovered() const {

	for(int i = 0; i < flatHeight; i++)
		for(int j = 0; j < flatWidth; j++) {
			if(!flat[i][j].isCovered())
				return false;
		}

	return true;
}

void Flat::setSensor(const Coordinate &c, Sensor s) {
	
	flat[c.y][c.x].setSensor(s);

	if(s == SENSOR_TH || s == SENSOR_FD) {

		for(int j = (c.x + 1); j < flatWidth; j++) {

			if(flat[c.y][j].isWall())
				break;
			
			flat[c.y][j].setBeams(flat[c.y][j].getBeams() + 1);
		}

		for(int j = (c.x - 1); j > -1; j--) {

			if(flat[c.y][j].isWall())
				break;

			flat[c.y][j].setBeams(flat[c.y][j].getBeams() + 1);
		}
	}

	if(s == SENSOR_TV || s == SENSOR_FD) {

		for(int i = (c.y + 1); i < flatHeight; i++) {

			if(flat[i][c.x].isWall())
				break;

			flat[i][c.x].setBeams(flat[i][c.x].getBeams() + 1);
		}

		for(int i = (c.y - 1); i > -1; i--) {

			if(flat[i][c.x].isWall())
				break;

			flat[i][c.x].setBeams(flat[i][c.x].getBeams() + 1);
		}
	}
}

void Flat::clearSensor(const Coordinate& c) {

	int s = flat[c.y][c.x].getSensor();

	flat[c.y][c.x].clearSensor();

	if(s == SENSOR_TH || s == SENSOR_FD) {

		for(int j = (c.x + 1); j < flatWidth; j++) {

			if(flat[c.y][j].isWall())
				break;
			
			flat[c.y][j].setBeams(flat[c.y][j].getBeams() - 1);
		}

		for(int j = (c.x - 1); j > -1; j--) {

			if(flat[c.y][j].isWall())
				break;

			flat[c.y][j].setBeams(flat[c.y][j].getBeams() - 1);
		}
	}

	if(s == SENSOR_TV || s == SENSOR_FD) {

		for(int i = (c.y + 1); i < flatHeight; i++) {

			if(flat[i][c.x].isWall())
				break;

			flat[i][c.x].setBeams(flat[i][c.x].getBeams() - 1);
		}

		for(int i = (c.y - 1); i > -1; i--) {

			if(flat[i][c.x].isWall())
				break;

			flat[i][c.x].setBeams(flat[i][c.x].getBeams() - 1);
		}
	}
}
